﻿using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

using vJine.Core.Task;
using vJine.Core.Log;
using vJine.Core.IoC;

namespace vJine.Core.MQ {
    public class MessageServer<T> {
        string _host = "";
        public string host {
            get {
                if (string.IsNullOrEmpty(this._host)) {
                    return "0.0.0.0";
                }

                return this._host;
            }
            protected set {
                this._host = value;
            }
        }

        int _port = 1239;
        public int port {
            get {
                if (this._port == 0) {
                    return 1239;
                }

                return this._port;
            }
            protected set {
                this._port = value;
            }
        }

        bool IsRunning = true;

        #region Server
        public MessageServer()
            : this(1239) {
        }

        public MessageServer(int port)
            : this("0.0.0.0", port) {

        }

        public MessageServer(string local, int port)
            : this(local, port, 100) {

        }

        TaskQueue<MessageClient<T>> MQ_Clients = null;
        TaskQueue<MessagePackage<T>> PMQ_Send = null, PMQ_Receive = null;
        public event ClientArrivedEventHandler<T> ClientArrived;
        public MessageServer(string host, int port, int backlog) {
            this.host = host;
            this.port = port;

            this.PMQ_Receive = new TaskQueue<MessagePackage<T>>();
            this.MQ_Clients =
                new TaskQueue<MessageClient<T>>(10, 20, () => { //Service
                    TcpListener tlDaemon = null;
                    try {
                        tlDaemon =
                            vJine.Core.Utility.Net.CheckAndCreateListener(this.host, this.port, backlog <= 0 ? 100 : backlog);

                        while(true) {
                            TcpClient tc = tlDaemon.AcceptTcpClient();

                            this.NewClient(tc.GetStream());

                            if(!this.IsRunning) {
                                tlDaemon.Server.Blocking = true;
                                tlDaemon.Stop();
                                tlDaemon = null;

                                return;
                            }
                        }
                    } catch(Exception ex) {
                        if(!this.IsRunning) {
                            return;
                        }

                        throw ex;
                    }
                }, (MessageClient<T> mClient) => { //Message Receiver
                    MessageClient<T>.service_daemon(this.MQ_Clients.Enqueue, this.PMQ_Receive.Enqueue, mClient);
                }, null);

            this.PMQ_Send =
                new TaskQueue<MessagePackage<T>>(10, 10, null, (MessagePackage<T> M) => {
                    M.Write(M);
                }, "Write");
        }

        public void NewClient(NetworkStream Stream) {
            if (!this.IsRunning) {
                throw new CoreException("Message Queue Server Is Stopped");
            }

            MessageClient<T> newClient = new MessageClient<T>(Stream, this.PMQ_Send, this.PMQ_Receive);

            if (this.ClientArrived == null) {
                newClient.Close();
            }

            ClientArrivedEventArgs<T> Arg = new ClientArrivedEventArgs<T>() { Queue = newClient };
            this.ClientArrived(Arg);

            if (Arg.Cancel) {
                newClient.Close(); return;
            }

            this.MQ_Clients.Enqueue(newClient);
        }
        #endregion

        #region Actions

        public void Start() {
            this.Start(null);
        }

        public virtual void Start(ClientArrivedEventHandler<T> newClient) {
            this.MQ_Clients.Start();
            this.PMQ_Send.Start();

            if (newClient != null) {
                this.ClientArrived += newClient;
            }

            if (this.ClientArrived == null || this.ClientArrived.GetInvocationList().Length == 0) {
                throw new MQException("Start Error:No ClientArrived Events Bound");
            }
        }

        public void Stop() {
            this.IsRunning = false;

            this.MQ_Clients.Stop();
            this.PMQ_Receive.Stop();
        }
        #endregion
    }
}